package com.yjhh.chdataservice.service;

import com.yjhh.chdataservice.InitDataFactory;
import com.yjhh.chdataservice.beans.DataCheckVo;
import com.yjhh.chdataservice.beans.DeptBean;
import com.yjhh.chdataservice.beans.DeptInfoVo;
import com.yjhh.chdataservice.beans.MoveTypeDataVo;
import com.yjhh.chdataservice.except.YJHHException;
import com.yjhh.chdataservice.util.CommonResultData;
import com.yjhh.chdataservice.util.DataConsts;
import com.yjhh.chdataservice.component.ESClientManager;
import lombok.extern.log4j.Log4j;
import lombok.extern.log4j.Log4j2;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.ListenableActionFuture;
import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.PrefixQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.sum.InternalSum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.prefixQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;

/**
 * Created by hlantian on 2017/9/26.
 */
@Service
@Log4j2
public class LogicServiceImpl implements LogicService {

    @Autowired
    private ESClientManager esClientManager;

    @Autowired
    private InitDataFactory initDataFactory;

    @Override
    public String findFamilyMemberById(String id) {
        TransportClient client = null;
        try {
            client = esClientManager.getESClient();
            GetRequestBuilder prepareGet = client.prepareGet("familys", "family", id);
            ListenableActionFuture<GetResponse> execute = prepareGet.execute();
            GetResponse getResponse = execute.get();
            return getResponse.getSourceAsString();
        } catch (InterruptedException e) {
            log.error("findFamilyMemberById",e);
        } catch (ExecutionException e) {
            log.error("findFamilyMemberById",e);
        }
        
        return null;
    }

    @Override
    public CommonResultData<List<DeptInfoVo>> getMoveTopByAreaCode(String areaCode) throws YJHHException {
        DeptBean sy_dept_info = getSysDeptInfo(areaCode);
        PrefixQueryBuilder areacodequery = prefixQuery("deptno", sy_dept_info.getDeptno());
        SearchRequestBuilder searchRequestBuilder = esClientManager.getESClient().prepareSearch("obj_reloca_info").setTypes("reloca_info").setSize(0)
                .setQuery(areacodequery);
        TermsAggregationBuilder depts = AggregationBuilders.terms("depts");
        if (depts == null) {
            return new CommonResultData<List<DeptInfoVo>>(HttpStatus.OK.value(), "no data", null);
        }
        String deptID = DataConsts.getDeptID(sy_dept_info.getDeptLevel() + 1);
        if (deptID == null || "".equals(deptID.trim())) {
            return new CommonResultData<List<DeptInfoVo>>(HttpStatus.OK.value(), "no data", null);
        }
        TermsAggregationBuilder deptsAgg = depts.field(deptID)
                .missing(DataConsts.UNKNOWN).size(100);

        TermsAggregationBuilder removeTypeAgg = AggregationBuilders.terms("removaltype").field("removaltypename.keyword");
        removeTypeAgg.subAggregation(AggregationBuilders.sum("peoples").field("familyMemberCount"));
        removeTypeAgg.subAggregation(AggregationBuilders.count("familys").field("familyMemberCount"));
        deptsAgg.subAggregation(removeTypeAgg);

        searchRequestBuilder.addAggregation(deptsAgg);

        log.info("reqeust: " + searchRequestBuilder.toString());

        SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
        String jsonStr = searchResponse.toString();
        log.info("response:" + jsonStr);

        List<DeptInfoVo> deptInfoVoList = parseMoveTopData(jsonStr);
        return new CommonResultData<List<DeptInfoVo>>(HttpStatus.OK.value(), "suc", deptInfoVoList);
    }

    @Override
    public CommonResultData<List<DataCheckVo>> getDataFillTopByAreaCode(String areaCode) throws YJHHException {
        List<DataCheckVo> dataCheckVoList = new ArrayList<>();

        DeptBean syDeptInfo = getSysDeptInfo(areaCode);

        List<String> checkDataOneData = getCheckDataOneData(syDeptInfo);

        Map<String, JSONObject> deptInfoMap = initDataFactory.getDeptInfoMap();

        PrefixQueryBuilder areacodequery = prefixQuery("deptno", syDeptInfo.getDeptno());
        SearchRequestBuilder searchRequestBuilder = esClientManager.getESClient().prepareSearch("check_info").setTypes("info").setSize(0).setQuery
                (areacodequery);

        TermsAggregationBuilder depts = AggregationBuilders.terms("depts");
        if (depts == null) {
            return new CommonResultData<>(HttpStatus.OK.value(), "no data", null);
        }
        TermsAggregationBuilder deptsAgg = depts.field(DataConsts.getDeptID(syDeptInfo.getDeptLevel() + 1)).missing
                (DataConsts.UNKNOWN).size(100);


        for (String filedName : checkDataOneData) {
            deptsAgg.subAggregation(AggregationBuilders.sum(filedName).field(filedName));
        }

        searchRequestBuilder.addAggregation(deptsAgg);

        log.info("getDataFillTopByAreaCode reqeust: " + searchRequestBuilder.toString());

        SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();

        log.info("getDataFillTopByAreaCode response:" + searchResponse.toString());

        Aggregations aggregations = searchResponse.getAggregations();
        List<Aggregation> aggsList = aggregations.asList();
        StringTerms aggregation = (StringTerms) aggsList.get(0);
        List<StringTerms.Bucket> buckets = aggregation.getBuckets();

        for (StringTerms.Bucket bucket : buckets) {
            DataCheckVo dataCheckVo = new DataCheckVo();

            String deptId = bucket.getKeyAsString();
            Long recordCount = bucket.getDocCount();

            Aggregations bucketAggregations = bucket.getAggregations();
            List<Aggregation> aggregationList = bucketAggregations.asList();
            Integer fieldCount = aggregationList.size();

            Long totalBaseFiledCount = fieldCount * recordCount;
            Double totalFillFieldCount = 0.0;
            for (Aggregation aggs : aggregationList) {
                InternalSum internalSum = (InternalSum) aggs;
                String name = internalSum.getName();
                Double sumValue = internalSum.getValue();
                totalFillFieldCount += sumValue;
            }

            dataCheckVo.setDeptId(deptId);
            if (deptInfoMap.containsKey(deptId)) {
                dataCheckVo.setAreaName(deptInfoMap.get(deptId).getString("deptname"));
                dataCheckVo.setAreaCode(deptInfoMap.get(deptId).getString("areacode"));
            }
            dataCheckVo.setFillFiledCount(totalFillFieldCount);
            dataCheckVo.setBaseFiledCount(totalBaseFiledCount.doubleValue());

            BigDecimal bigDecimal = new BigDecimal(totalFillFieldCount / totalBaseFiledCount);
            double percent = bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            dataCheckVo.setPercent(percent);
            dataCheckVo.setDataCount(recordCount);

            dataCheckVoList.add(dataCheckVo);
        }

        Collections.sort(dataCheckVoList, new Comparator<DataCheckVo>() {

            @Override
            public int compare(DataCheckVo o1, DataCheckVo o2) {
                DataCheckVo dataCheckVo1 = (DataCheckVo) o1;
                DataCheckVo dataCheckVo2 = (DataCheckVo) o2;
                return dataCheckVo2.getPercent().compareTo(dataCheckVo1.getPercent());
            }
        });


        return new CommonResultData<>(HttpStatus.OK.value(), "suc", dataCheckVoList);
    }

    /**
     * 获取需要检测的字段名称集合
     *
     * @param syDeptInfo
     * @return
     * @throws YJHHException
     */
    private List<String> getCheckDataOneData(DeptBean syDeptInfo) throws YJHHException {
        List<String> checkFieldList = new ArrayList<>();

        PrefixQueryBuilder areacodequery = prefixQuery("deptno", syDeptInfo.getDeptno());
        SearchRequestBuilder searchRequestBuilder = esClientManager.getESClient().prepareSearch("check_info").setTypes("info").setSize(1).setQuery
                (areacodequery);

        log.info("getCheckDataOneData reqeust: " + searchRequestBuilder.toString());

        SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();

        String resultJson = searchResponse.toString();

        log.info("getCheckDataOneData response:" + searchResponse.toString());
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        if (searchHits != null && searchHits.length > 0) {
            SearchHit searchHit = searchHits[0];
            Map<String, Object> source = searchHit.getSource();
            Set<String> filedSet = source.keySet();
            for (String filed : filedSet) {
                if (StringUtils.startsWithIgnoreCase(filed, "a_")
                        || StringUtils.startsWithIgnoreCase(filed, "b_")
                        || StringUtils.startsWithIgnoreCase(filed, "c_")
                        || StringUtils.startsWithIgnoreCase(filed, "d_")) {
                    checkFieldList.add(filed);
                }
            }
        }
        return checkFieldList;
    }

    /**
     * 根据ES返回的结果解析移民搬迁的数据
     *
     * @param jsonStr es返回的数据
     * @return
     */
    private List<DeptInfoVo> parseMoveTopData(String jsonStr) {
        List<DeptInfoVo> deptInfoVoList = new ArrayList<>();

        Map<String, JSONObject> deptInfoMap = initDataFactory.getDeptInfoMap();

        JSONObject rootJsonObject = JSONObject.fromObject(jsonStr);
        JSONObject aggregations = rootJsonObject.getJSONObject("aggregations");
        JSONObject depts = aggregations.getJSONObject("depts");
        JSONArray buckets = depts.getJSONArray("buckets");
        for (int i = 0; i < buckets.size(); i++) {
            DeptInfoVo deptInfoVo = new DeptInfoVo();
            JSONObject bucket = (JSONObject) buckets.get(i);
            String deptId = bucket.getString("key");
            int areaFamilyCount = bucket.getInt("doc_count");
            deptInfoVo.setDeptId(deptId);
            if (deptInfoMap.containsKey(deptId)) {
                deptInfoVo.setAreaName(deptInfoMap.get(deptId).getString("deptname"));
                deptInfoVo.setAreaCode(deptInfoMap.get(deptId).getString("areacode"));
            }
            deptInfoVo.setFamilyNum(areaFamilyCount);
            deptInfoVo.setPeopleNum(0);

            List<MoveTypeDataVo> moveTypeDataVoList = new ArrayList<>();
            JSONObject removeTypeJsonObject = bucket.getJSONObject("removaltype");
            JSONArray removeTypeBuckets = removeTypeJsonObject.getJSONArray("buckets");
            for (int j = 0; j < removeTypeBuckets.size(); j++) {
                MoveTypeDataVo moveTypeDataVo = new MoveTypeDataVo();
                JSONObject removeTypeBucket = (JSONObject) removeTypeBuckets.get(j);
                String typeName = removeTypeBucket.getString("key");
                JSONObject peopleCountObje = removeTypeBucket.getJSONObject("peoples");
                JSONObject familyCountObj = removeTypeBucket.getJSONObject("familys");
                int peopleCount = peopleCountObje.getInt("value");
                int familyCount = familyCountObj.getInt("value");

                moveTypeDataVo.setFamilyNum(familyCount);
                moveTypeDataVo.setPeopleNum(peopleCount);
                moveTypeDataVo.setMoveType(typeName);
                deptInfoVo.setPeopleNum(deptInfoVo.getPeopleNum() + peopleCount);

                moveTypeDataVoList.add(moveTypeDataVo);
            }
            deptInfoVo.setMoveTypeDataVo(moveTypeDataVoList);
            deptInfoVoList.add(deptInfoVo);
        }
        return deptInfoVoList;
    }

    private DeptBean getSysDeptInfo(String areacode) throws YJHHException {
        BoolQueryBuilder areacodequery = boolQuery().must(termQuery("areacode", areacode));
        SearchRequestBuilder searchRequestBuilder = esClientManager.getESClient()
                .prepareSearch("sy_dept_info")
                .setTypes("sy_dept")
                .setSize(10)
                .setQuery(areacodequery);
        SearchResponse searchResponse = null;
        log.info("reqeust: " + searchRequestBuilder.toString());
        searchResponse = searchRequestBuilder.execute().actionGet();
        DeptBean deptBean = new DeptBean();
        if (searchResponse.getHits().getTotalHits() > 0) {
            SearchHit at = searchResponse.getHits().getAt(0);
            deptBean.setDeptLevel(Integer.parseInt(at.getSource().get("deptlevel").toString()));
            deptBean.setDeptID(at.getId());
            deptBean.setDeptno(at.getSource().get("deptno").toString());
        } else {
            throw new YJHHException("No Dept Data Find");
        }
        return deptBean;
    }

}
